package cn.ad.server.web.spring.service.httpclient4;

import cn.ad.server.web.spring.service.components.http.client.*;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.ProtocolVersion;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.PreDestroy;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/**
 * 使用httpclient4执行请求。
 * @author wangwei
 *
 */
public class HttpClientExecutor implements AdHttpExecutor {

	private static Logger LOGGER = LoggerFactory.getLogger(HttpClientExecutor.class);

	/**
	 * HttpClient4
	 */
	private CloseableHttpClient httpclient;
	
	/**
	 * 全局的RequestConfig
	 */
    private RequestConfig requestConfig;
    

	public void setHttpclient(CloseableHttpClient httpclient) {
		this.httpclient = httpclient;
	}
	
	public CloseableHttpClient getHttpclient() {
		return httpclient;
	}
	

	public RequestConfig getRequestConfig() {
		return requestConfig;
	}

	public void setRequestConfig(RequestConfig requestConfig) {
		this.requestConfig = requestConfig;
	}

	public HttpClientExecutor() {
	}

	@Override
	public final AdHttpResponse<?> execute(AdHttpGetRequest adHttpGetRequest) throws ResponseHandleException, IOException {
		return execute(adHttpGetRequest, null);
	}

	@Override
	public final AdHttpResponse<?> execute(AdHttpPostRequest adHttpPostRequest) throws ResponseHandleException, IOException {
		return execute(adHttpPostRequest, null);
	}
	
	
	@Override
	public final <T> AdHttpResponse<T> execute(AdHttpPostRequest adHttpPostRequest,
			AdResponseHandler<? extends T> responseHandler) throws ResponseHandleException, IOException {
		String uri = adHttpPostRequest.getUrl();
		HttpPost post = new HttpPost(uri);
		Map<String, Object> headerParams = adHttpPostRequest.getHeaderParams();
		addHeader(post, headerParams);
		
		//自定义config
		if(adHttpPostRequest.getTimeOut()!=-1){			
			RequestConfig config = generatConfig(adHttpPostRequest.getTimeOut());
			post.setConfig(config);
		}
		
		switch (adHttpPostRequest.getType()) {// UNKNOWN,JSON,FORM,BYTE
		case UNKNOWN:
			break;
		case JSON:
			setEntity(post, adHttpPostRequest.getJson());
			break;
		case FORM:
			setEntity(post, adHttpPostRequest.getParams());
			break;
		case BYTE:
			//TODO CONTENT_TYPE 此时是否重复添加
			Object contentType = "";
			if(headerParams!=null){
				contentType = headerParams.get(HTTP.CONTENT_TYPE);
			}
			setEntity(post, adHttpPostRequest.getBuffer(), contentType);
			break;
		default:
			break;
		}

		AdHttpResponse<T> adresp = new AdHttpResponse<T>();
		long time =0l;
		long executeStart = System.currentTimeMillis();
		try {
			CloseableHttpResponse response = httpclient.execute(post, (HttpContext) null);
			long executeEnd = System.currentTimeMillis();
			time = executeEnd - executeStart;
			adresp.setStatusCode(response.getStatusLine().getStatusCode());
			ProtocolVersion protocolVersion = response.getProtocolVersion();
			if(protocolVersion!=null){				
				adresp.setProtocol(protocolVersion.getProtocol());
				adresp.setMajor(protocolVersion.getMajor());
				adresp.setMinor(protocolVersion.getMinor());
			}
			if (responseHandler == null) {
				return adresp;
			}
			try {
				handleEntity(responseHandler, response, adresp);
				return adresp;
			} finally {
				response.close();
			}
		} catch (ResponseHandleException e) {
			adresp.setStatusCode(HANDLER_ERROR_CODE);
			long executeEnd = System.currentTimeMillis();
			time = executeEnd - executeStart;
			adresp.setRespTime(time);
			//LOGGER.error("执行请求失败：" + e.getMessage(), e);
			throw e;
		} catch (IOException e) {
			adresp.setStatusCode(OI_ERROR_CODE);
			long executeEnd = System.currentTimeMillis();
			time = executeEnd - executeStart;
			adresp.setRespTime(time);
			//LOGGER.error("执行请求失败：" + e.getMessage(), e);
			throw e;
		}finally{
			doExecuteEnd(adHttpPostRequest,adresp);
		}
	}

	/**
	 * 处理entity
	 * 
	 * @param responseHandler
	 * @param response
	 * @param adresp
	 * @throws Exception
	 * @throws IOException
	 */
	private final <T> void handleEntity(AdResponseHandler<? extends T> responseHandler, CloseableHttpResponse response,
			AdHttpResponse<T> adresp) throws ResponseHandleException, IOException {
		try {
			final T result = responseHandler.doHandle(response);
			final HttpEntity entity = response.getEntity();
			adresp.setLength(entity.getContentLength());
			EntityUtils.consume(entity);
			adresp.setResult(result);
		} catch (final ResponseHandleException t) {
			final HttpEntity entity = response.getEntity();
			try {
				EntityUtils.consume(entity);
			} catch (final Exception t2) {
				LOGGER.warn("Error consuming content after an exception.", t2);
			}
			throw t;
		}
	}

	@Override
	public final <T> AdHttpResponse<T> execute(AdHttpGetRequest adHttpGetRequest,
			AdResponseHandler<? extends T> responseHandler) throws ResponseHandleException, IOException {
		HttpGet httpGet = getHttpGet(adHttpGetRequest.getUrl(), adHttpGetRequest.getParams());

		if(adHttpGetRequest.getTimeOut()!=-1){			
			RequestConfig config = generatConfig(adHttpGetRequest.getTimeOut());
			httpGet.setConfig(config);
		}
		
		AdHttpResponse<T> adresp = new AdHttpResponse<T>();
		long time =0l;
		long executeStart = System.currentTimeMillis();
		try {
			CloseableHttpResponse response = httpclient.execute(httpGet, (HttpContext) null);
			long executeEnd = System.currentTimeMillis();
			time = executeEnd - executeStart;
			adresp.setRespTime(time);
			adresp.setStatusCode(response.getStatusLine().getStatusCode());
			ProtocolVersion protocolVersion = response.getProtocolVersion();
			if(protocolVersion!=null){				
				adresp.setProtocol(protocolVersion.getProtocol());
				adresp.setMajor(protocolVersion.getMajor());
				adresp.setMinor(protocolVersion.getMinor());
			}
			if (responseHandler == null) {
				return adresp;
			}
			try {
				handleEntity(responseHandler, response, adresp);
				return adresp;
			} finally {
				response.close();
			}
		} catch (ResponseHandleException e) {
			adresp.setStatusCode(HANDLER_ERROR_CODE);
			long executeEnd = System.currentTimeMillis();
			time = executeEnd - executeStart;
			adresp.setRespTime(time);
			//LOGGER.error("执行请求失败：" + e.getMessage(), e);
			throw e;
		} catch (IOException e) {
			long executeEnd = System.currentTimeMillis();
			time = executeEnd - executeStart;
			adresp.setRespTime(time);
			adresp.setStatusCode(OI_ERROR_CODE);
			//LOGGER.error("执行请求失败：" + e.getMessage(), e);
			throw e;
		}finally{
			doExecuteEnd(adHttpGetRequest,adresp);
		}
	}
	
	/**
	 * 添加请求头信息
	 * @param post
	 * @param headerParams
	 */
	private void addHeader(HttpPost post,Map<String, Object> headerParams){
		if(post==null){
			return ;
		}
		if(headerParams==null){
			return ;
		}
		Set<String> keySet = headerParams.keySet();
		for (String key : keySet) {
			if(headerParams.get(key)!=null){
				post.addHeader(key, headerParams.get(key).toString());
			}
		}
	}

	/**
	 * 添加form表单
	 * @param httpPost
	 * @param params
	 */
	private void setEntity(HttpPost httpPost, Map<String, Object> params) {
		if (params == null || params.isEmpty()) {
			return;
		}
		// httpPost.setConfig(requestConfig);
		List<NameValuePair> pairList = new ArrayList<>(params.size());
		for (Entry<String, Object> entry : params.entrySet()) {
			NameValuePair pair = new BasicNameValuePair(entry.getKey(), entry.getValue().toString());
			pairList.add(pair);
		}
		httpPost.setEntity(new UrlEncodedFormEntity(pairList, Charset.forName("UTF-8")));
	}

	/**
	 * 获取HttpGet
	 * @param url
	 * @param params
	 * @return
	 */
	private HttpGet getHttpGet(String url, Map<String, Object> params) {
		validateUrl(url);
		String apiUrl = url;
		StringBuffer param = new StringBuffer();
		int i = 0;
		if (params==null) {
			HttpGet httpGet = new HttpGet(apiUrl);
			return httpGet;
		}
		for (String key : params.keySet()) {
			if (i == 0)
				param.append("?");
			else
				param.append("&");
			param.append(key).append("=").append(params.get(key));
			i++;
		}
		apiUrl += param;
		HttpGet httpGet = new HttpGet(apiUrl);
		return httpGet;
	}
	
	/**
	 * 验证url
	 * @param url
	 */
	 void validateUrl(String url){
		 
	 }

	/**
	 * 添加json
	 * @param httpPost
	 * @param json
	 */
	private void setEntity(HttpPost httpPost, String json) {
		if (json == null || json.length() == 0) {
			return;
		}
		StringEntity stringEntity = new StringEntity(json.toString(), "UTF-8");// 解决中文乱码问题
		stringEntity.setContentEncoding("UTF-8");
		stringEntity.setContentType("application/json");
		httpPost.setEntity(stringEntity);
	}
	
	/**
	 * 添加byte[]
	 * @param httpPost
	 * @param buffer
	 * @param contentType
	 */
	private void setEntity(HttpPost httpPost, byte[] buffer,Object contentType) {
		if (buffer == null || buffer.length == 0) {
			return;
		}
		ByteArrayEntity entity = new ByteArrayEntity(buffer);
		entity.setContentEncoding("UTF-8");
		if(contentType==null||contentType.equals("")){
			LOGGER.error("no specified content-Type");
			throw new AvailableArgumentException("no specified content-Type");
		}
		entity.setContentType(contentType.toString());
		httpPost.setEntity(entity);
	}


	
	 @PreDestroy
	public void shutdown(){
		if(httpclient==null){
			return ;
		}
		try {
			httpclient.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	 
	/**
	 * 请求后续执行
	 * @param <T>
	 * @param request
	 * @param response
	 */
	public <T> void doExecuteEnd(AdHttpGetRequest request,AdHttpResponse<T> response){
		
	}
	/**
	 * 请求后续执行
	 * @param <T>
	 * @param request
	 * @param response
	 */
	public <T> void doExecuteEnd(AdHttpPostRequest request,AdHttpResponse<T> response){
		
	}
	
	/**
	 * 生成RequestConfig，继承全局的RequestConfig。
	 * @param timeout
	 * @return
	 */
	private  RequestConfig  generatConfig(int timeout){
		RequestConfig.Builder configBuilder = RequestConfig.custom();
		configBuilder.setConnectionRequestTimeout(requestConfig.getConnectionRequestTimeout())
		.setConnectTimeout(requestConfig.getConnectTimeout())
		.setCookieSpec(requestConfig.getCookieSpec())
		.setLocalAddress(requestConfig.getLocalAddress())
		.setMaxRedirects(requestConfig.getMaxRedirects())
		.setProxy(requestConfig.getProxy())
		.setProxyPreferredAuthSchemes(requestConfig.getProxyPreferredAuthSchemes())
		.setSocketTimeout(timeout)
		.setTargetPreferredAuthSchemes(requestConfig.getTargetPreferredAuthSchemes())
		.setAuthenticationEnabled(requestConfig.isAuthenticationEnabled())
		.setCircularRedirectsAllowed(requestConfig.isCircularRedirectsAllowed())
		.setContentCompressionEnabled(requestConfig.isContentCompressionEnabled())
		.setExpectContinueEnabled(requestConfig.isExpectContinueEnabled())
		.setRedirectsEnabled(requestConfig.isRedirectsEnabled())
		.setRelativeRedirectsAllowed(requestConfig.isRelativeRedirectsAllowed());
//		requestConfig.isDecompressionEnabled();
//		requestConfig.isStaleConnectionCheckEnabled();
		return configBuilder.build();
	}
	
	
}
